package com.cooksys.film_rental.dao.impl;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.cooksys.film_rental.dao.CityDao;
import com.cooksys.film_rental.model.Address;
import com.cooksys.film_rental.model.City;
import com.cooksys.film_rental.model.Country;

@Repository("CityDao")
@Transactional
public class CityDaoImpl implements CityDao {

	@Autowired(required = true)
	private SessionFactory sessionFactory;

	/**
	 * 
	 * @return the current session from the internal {@link SessionFactory}
	 */
	public Session currentSession() {
		return sessionFactory.getCurrentSession();
	}

	@Override
	public City save(City city) {
		Session session = currentSession();
		Serializable id = session.save(city);
		return (City) session.get(City.class, id);

	}

	@Override
	public void update(City city) {
		Session session = currentSession();
		session.update(city);

	}

	@Override
	public void delete(City city) {
		Session session = currentSession();
		session.delete(city);

	}

	@Override
	public City getCityByCityId(Short cityId) {
		Session session = currentSession();

		// returns a record from the CITY table as a City object based
		// on the given cityId, the table's primary key
		String hql = "from City where cityId = :cityId";
		Query query = session.createQuery(hql);
		query.setShort("cityId", cityId);

		return (City) query.list().get(0);
	}

	@Override
	public City getCityByAddress(Address address) {
		Session session = currentSession();

		// selects the city associated with the given address object
		String hql = "select a.city from Address a where a = :address";
		Query query = session.createQuery(hql);
		query.setEntity("address", address);

		return (City) query.list().get(0);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<City> searchCitiesByCountry(Country country) {
		Session session = currentSession();

		// selects the city objects associated with the given country
		String hql = "from City c where c.country = :country";
		Query query = session.createQuery(hql);
		query.setEntity("country", country);

		return query.list();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<City> searchCitiesByPartialCityName(String partialCityName) {
		Session session = currentSession();

		// split the search string into search terms
		String[] cityNameFragments = partialCityName.split("\\s");

		// 'select' statement of the hql query
		String hql = "from City c where ";

		// generating the where clause
		for (int i = 0; i < cityNameFragments.length; i++) {

			// for each search term, add a segment of the where clause that
			// checks city column of the CITY table for matches
			hql += "c.city like :cityNameFragment" + i;

			if (i < cityNameFragments.length - 1) {

				// concatenate these where clause segments with an 'or' operator
				hql += " or ";

			}

		}

		Query query = session.createQuery(hql);

		for (int i = 0; i < cityNameFragments.length; i++) {

			// parameterize the query with the search terms
			query.setString("cityNameFragment" + i, "%" + cityNameFragments[i]
					+ "%");

		}

		return query.list();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<City> getAllCities() {
		Session session = currentSession();

		String hql = "from City";

		Query query = session.createQuery(hql);

		return query.list();
	}

	@Override
	public void saveOrUpdate(City city) {
		Session session = currentSession();

		session.saveOrUpdate(city);

	}

	@Override
	public City getCityByInfoAndCountry(String city, Country country) {
		Session session = currentSession();

		String hql = "from City c where c.city = :cityName and c.country = :country";
		Query query = session.createQuery(hql);
		query.setString("cityName", city);
		query.setEntity("country", country);

		return (City) query.uniqueResult();
	}

}
